home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
SGI Developer Toolbox 6.1
/
SGI Developer Toolbox 6.1 - Disc 4.iso
/
src
/
demos
/
VGX
/
morph
/
morph.c
< prev
next >
Wrap
C/C++ Source or Header
|
1994-08-01
|
44KB
|
1,548 lines
/*
* Copyright 1991, 1992, 1993, 1994, Silicon Graphics, Inc.
* All Rights Reserved.
*
* This is UNPUBLISHED PROPRIETARY SOURCE CODE of Silicon Graphics, Inc.;
* the contents of this file may not be disclosed to third parties, copied or
* duplicated in any form, in whole or in part, without the prior written
* permission of Silicon Graphics, Inc.
*
* RESTRICTED RIGHTS LEGEND:
* Use, duplication or disclosure by the Government is subject to restrictions
* as set forth in subdivision (c)(1)(ii) of the Rights in Technical Data
* and Computer Software clause at DFARS 252.227-7013, and/or in similar or
* successor clauses in the FAR, DOD or NASA FAR Supplement. Unpublished -
* rights reserved under the Copyright Laws of the United States.
*/
/*
morph.c - Use VGX texture mapping to do image metamorphosis.
Tim Heidmann, Silicon Graphics
Created May 1, 1991
Last Edit November 25, 1991
*/
/*
Morph'ing is a technique and term first used in production by Industrial
Light and Magic for the film "Willow". Real-time morph'ing using texture
maps is from an idea by Michael Wahrman. Originally written for the
George Coates Performance Works theatrical project "Invisible Site".
*/
#include <stdio.h>
#include <math.h>
#include <gl.h>
#include <device.h>
#include <gl/image.h>
/* Window, mouse, and events */
long xOrig, yOrig;
long winWidth = 512, winHeight = 512;
float winWidthFactor, winHeightFactor;
long performXOrig, performYOrig, performXSize = -1, performYSize;
float performAspect;
#define MINDRAGDIST 0.01
#define MAXHITDIST 0.02
#define RGB_GRAY 0x00505050
#define RGB_RED 0x000000a0
#define RGB_GREEN 0x0000a000
#define RGB_BLUE 0x00a00000
#define RGB_YELLOW 0x0000a0a0
#define RGB_CYAN 0x00a0a000
#define RGB_MAGENTA 0x00a000a0
#define RGB_WHITE 0x00ffffff
#define RGB_BLACK 0x00000000
#define RGB_BRIGHTER 0x00505050
long colorList[] =
{RGB_RED, RGB_GREEN, RGB_BLUE, RGB_YELLOW, RGB_CYAN, RGB_MAGENTA};
long triColorList[] =
{RGB_RED, RGB_GREEN, RGB_BLUE, RGB_YELLOW, RGB_CYAN, RGB_MAGENTA};
float tev_val[] = {TV_NULL};
float tex_val[] =
{TX_MINFILTER, TX_BILINEAR, TX_MAGFILTER, TX_BILINEAR, TX_NULL};
#define MINPADKEY PAD1
#define MAXPADKEY PAD9
/* Button numbers for numeric pad keys:
0 1 2 3 4 5 6 7 8 9 */
int buttonNum[] = {59, 58, 64, 65, 63, 69, 70, 67, 68, 75};
/* Program state */
enum {selmoveVertices, addVertices, connectVertices}
clickMode = selmoveVertices;
int performFlag = TRUE;
int fullScreen = FALSE;
int shiftDown = 0;
long mainMenu, vColorMenu, cColorMenu, targetGotoMenu, targetDeleteMenu;
long performMenu;
int dbxVertex = -1, nDbxEdges = 0, dbxEdges[10];
long *bgImage = NULL;
int bgXsize = -1, bgYsize = -1;
int flushQueue = FALSE;
int autoRun = FALSE;
int showMesh = FALSE;
int showTwins = FALSE;
/* Working Mesh data structures */
float crossArm = 0.008;
#define MAXVERTICES 100
struct vertlist_tag {
float x, y;
long color;
int selected;
} vertexList[MAXVERTICES];
int nVertices = 0;
long vColor = RGB_YELLOW;
#define MAXCNX 600
struct cnxlist_tag {
int v0, v1;
long color;
int selected;
} cnxList[MAXCNX];
long nCnx = 0;
long cColor = RGB_BLUE;
#define MAXTRIANGLES 400
struct tri_tag {
int v0, v1, v2;
} triangleList[MAXTRIANGLES];
int nTriangles = 0;
/* Collection of target images and meshes */
#define MAXTARGETS 12
struct target_tag {
long *image;
struct vertlist_tag *vertexList;
struct cnxlist_tag *cnxList;
struct tri_tag *triangleList;
int xSize, ySize, nVertices, nCnx, nTriangles, textureIndex;
} target[MAXTARGETS];
int nTargets = 0;
int thisTarget = -1, destTarget = -1;
long thisTargetColor, destTargetColor;
struct vertlist_tag tmpVertexList[MAXVERTICES];
int nTmpVertices = 0;
int currentSpeed = 5;
/* Number of frames per transition for each numeric pad key:
0 1 2 3 4 5 6 7 8 9 */
int frameCount[] = {1, 5, 10, 15, 20, 30, 45, 60, 90, 120};
/* Temp structure for doing triangulation */
#define MAXEDGESPERVERTEX 17
struct {
int nEdges;
struct {
int index; /* Index into edge (cnx) list */
int marked; /* marked[i] TRUE => edge[i]-[i+1] angle tried. */
} edge[MAXEDGESPERVERTEX];
} vertexEdges[MAXVERTICES];
/* Selected data structure */
int selectedVList[MAXVERTICES];
int nVSelected = 0;
int selectedCList[MAXCNX];
int nCSelected = 0;
/* Prototypes */
void
ParseArgs(int c, char *v[]);
void
BadCommand(char *name);
void
DoClick(short mx, short my);
int
AddVertex(float x, float y);
void
DeleteVertex(int iVertex);
int
HitVertex(float x, float y);
void
AddSelectVertex(int iVertex);
void
SelectVertex(int iVertex);
void
DeleteVSelection(int iSelect);
void
MoveSelected(float dx, float dy);
void
DeleteSelectedVertices();
int
AddCnx(int v0, int v1);
void
DeleteCnx(int iCnx);
int
HitCnx(float x, float y);
void
AddSelectCnx(int iCnx);
void
SelectCnx(int iCnx);
void
DeleteCSelection(int iSelect);
void
DeleteSelectedCnx();
int
AddTriangle(int v0, int v1, int v2);
int
ReadLine(FILE *myf, char *buf);
int
ReadCnx(char *filename);
int
WriteCnx(char *filename);
void
CnxToTri();
/*******************************************************************************
Main
*******************************************************************************/
main(int argc, char *argv[]) {
short dev, val;
short initMouseX, initMouseY;
int iNumPadKey, iTarget;
int redrawFlag, doneFlag;
printf("\n Loading data -- please wait ...\n");
ParseArgs(argc, argv);
printf(" Finished loading data!\n\n");
InitGfx();
for (doneFlag=FALSE; !doneFlag;) {
if (performFlag) {
/* Performance mode */
if (flushQueue) {
while (qtest()) qread(&val);
flushQueue = FALSE;
autoRun = FALSE;
setbell(1); ringbell();
}
if (qtest()) switch (dev = qread(&val)) {
case ESCKEY:
doneFlag = TRUE;
break;
case RIGHTMOUSE:
autoRun = FALSE;
switch (dopup(performMenu)) {
case 1: /* To Edit Mode */
performFlag = FALSE;
SetEditGfx();
DrawWindow();
break;
case 2: /* Auto Run */
autoRun = TRUE;
break;
case 3: /* Toggle Show Mesh */
showMesh = !showMesh;
DrawPerformWindow();
break;
case 4: /* Toggle Twin Images */
showTwins = !showTwins;
performAspect = (showTwins ? 2.0 : 1.0) *
((float)performYSize)/performXSize;
DrawPerformWindow();
break;
case 99: /* Exit */
doneFlag = TRUE;
break;
}
break;
case REDRAW:
reshapeviewport();
getorigin(&performXOrig, &performYOrig);
getsize(&performXSize, &performYSize);
performAspect = (showTwins ? 2.0 : 1.0) *
((float)performYSize)/performXSize;
DrawPerformWindow();
break;
default:
if (dev>=F1KEY && dev<=F12KEY) {
/* Decode FKEY, or -1 for none. Initiate transition. */
if (val) {
autoRun = FALSE;
thisTarget = destTarget;
iTarget = dev - F1KEY;
destTarget = iTarget<nTargets ? iTarget : -1;
AnimateTransition();
}
}
else if (dev>=MINPADKEY && dev<=MAXPADKEY) {
/* Decode numeric pad button, set current speed */
if (val) {
autoRun = FALSE;
for (iNumPadKey=0; iNumPadKey<=9; iNumPadKey++)
if (buttonNum[iNumPadKey] == dev)
{currentSpeed = iNumPadKey; break;}
}
}
break;
}
if (autoRun && !qtest()) {
thisTarget = destTarget;
destTarget = (random() & 255) / 256.0 * nTargets;
currentSpeed = random() % 7 + 3; /* Select speed 3-9 */
AnimateTransition();
}
if (redrawFlag && !qtest()) {
DrawPerformWindow();
redrawFlag = FALSE;
}
}
else
/* Mesh editing mode */
switch (dev = qread(&val)) {
case LEFTMOUSE:
qread(&initMouseX); qread(&initMouseY);
if (val) DoClick(initMouseX, initMouseY);
break;
case RIGHTMOUSE:
if (val) doneFlag = DoMenu();
break;
case LEFTSHIFTKEY:
case RIGHTSHIFTKEY:
shiftDown = val;
break;
case BACKSPACEKEY:
/* Don't delete vertices in connect mode.
Reset selection too. */
if (clickMode == connectVertices) SelectVertex(-1);
DeleteSelectedVertices();
DeleteSelectedCnx();
DrawWindow();
break;
case REDRAW:
reshapeviewport();
getorigin(&xOrig, &yOrig);
DrawWindow();
break;
case ESCKEY:
doneFlag = TRUE;
break;
default:
break;
}
}
}
void
ParseArgs(int c, char *v[]) {
int i;
FILE *f;
char *cmdName, imgFile[100], cnxFile[100], buf[100];
int fileRead;
cmdName = v[0];
fileRead = FALSE;
for (c--, v++; c>0; c--, v++) {
if (v[0][0] != '-') {
if (fileRead) BadCommand(cmdName);
if ((f = fopen(v[0], "r")) == NULL)
fprintf(stderr, "Cannot open master file %s\n", v[0]);
else {
for (i=0; i<MAXTARGETS; i++) {
if (!ReadLine(f, buf)) break;
sscanf(buf, "%s %s", imgFile, cnxFile);
if (!ReadImg(imgFile)) continue;
if (ReadCnx(cnxFile) < 0) continue;
AddTarget();
}
fclose(f);
fileRead = TRUE;
}
} else /* v[0][0] == '-' */
switch (v[0][1]) {
case 'p': performFlag = TRUE; break;
case 'f': fullScreen = TRUE; performFlag = TRUE; break;
case 'e': performFlag = FALSE; break;
default: BadCommand(cmdName);
}
}
}
void
BadCommand(char *name) {
fprintf(stderr, "usage: %s [-p] [-f] [-e] [<master file>]\n", name);
exit(1);
}
InitGfx() {
int i;
/* Open the window */
#ifdef DEBUG
foreground();
#endif
if (performFlag) {
if (fullScreen)
prefposition(0, getgdesc(GD_XPMAX)-1, 0, getgdesc(GD_YPMAX)-1);
winopen("Morph");
getorigin(&performXOrig, &performYOrig);
getsize(&performXSize, &performYSize);
performAspect =
(showTwins ? 2.0 : 1.0) * ((float)performYSize)/performXSize;
/* Allow resizing the perform window */
winconstraints();
/* Position the edit window in the middle of the screen */
xOrig = (getgdesc(GD_XPMAX) - 1 - winWidth) / 2;
yOrig = (getgdesc(GD_YPMAX) - 1 - winHeight) / 2;
thisTarget = destTarget = -1;
tevdef(1, 0, tev_val);
SetPerformGfx();
} else {
prefsize(winWidth, winHeight);
winopen("Morph");
getorigin(&xOrig, &yOrig);
tevdef(1, 0, tev_val);
SetEditGfx();
}
winWidthFactor = 1.0 / winWidth;
winHeightFactor = 1.0 / winHeight;
/* Check if texture mapping supported. */
if( !getgdesc( GD_TEXTURE ) ) {
printf("\n\nWARNING -- This machine does not support texture mapping,\n");
printf(" which is required to perform the morphing.\n");
}
RGBmode();
doublebuffer();
gconfig();
ortho2(0.0, 1.0, 0.0, 1.0);
cpack(RGB_BLACK);
clear();
swapbuffers();
/* Queue the devices */
qdevice(LEFTMOUSE);
tie(LEFTMOUSE, MOUSEX, MOUSEY);
qdevice(RIGHTMOUSE);
qdevice(LEFTSHIFTKEY);
qdevice(RIGHTSHIFTKEY);
qdevice(BACKSPACEKEY);
for (i=F1KEY; i<=F12KEY; i++) qdevice(i);
qdevice(ESCKEY);
for (i=0; i<=9; i++) qdevice(buttonNum[i]);
/* Set up the menus */
mainMenu = defpup("");
vColorMenu = defpup("Vertex Color %t");
addtopup(vColorMenu,
"Red%x31|Green%x32|Blue%x33|Yellow%x34|Cyan%x35|Magenta%x36");
cColorMenu = defpup("Connection Color %t");
addtopup(cColorMenu,
"Red%x21|Green%x22|Blue%x23|Yellow%x24|Cyan%x25|Magenta%x26");
targetGotoMenu = defpup("No Targets %t");
targetDeleteMenu = defpup("No Targets %t");
performMenu = defpup("Morph %t");
addtopup(performMenu, "Edit Mode%x1%l|Auto Run%x2");
addtopup(performMenu, "Toggle Mesh%x3|Toggle Twin Images%x4%l|Exit %x99");
MakeTargetMenus();
MakeMenu();
}
SetEditGfx() {
int i;
/* Set up graphics for mesh editing.
Window to original size, border. Ortho projection. */
/*
winposition(xOrig, xOrig+winWidth-1, yOrig, yOrig+winHeight-1);
*/
prefsize(winWidth, winHeight);
winconstraints();
reshapeviewport();
ortho2(0.0, 1.0, 0.0, 1.0);
/* No texture mapping, no alphablending */
tevbind(TV_ENV0, 0);
blendfunction(BF_ONE, BF_ZERO);
}
SetPerformGfx() {
int i;
/* Set up graphics for performance. */
if (performXSize > 0)
winposition(performXOrig, performXOrig+performXSize-1,
performYOrig, performYOrig+performYSize-1);
winconstraints();
reshapeviewport();
/* Turn texture mapping on, alphablending on */
for (i=0; i<nTargets; i++)
texdef2d(target[i].textureIndex, 4,
target[i].xSize, target[i].ySize, target[i].image, 0, tex_val);
tevbind(TV_ENV0, 1);
blendfunction(BF_SA, BF_ONE);
}
void
DoClick(short mx, short my) {
int iVertex, iCnx;
float xFirst, yFirst, xNew, yNew, xLast, yLast, dist;
enum {starting, tracking, done} trackState;
xFirst = (mx - xOrig) * winWidthFactor;
yFirst = (my - yOrig) * winHeightFactor;
switch (clickMode) {
case selmoveVertices:
/* Act on the hit to select new vertices */
if ((iVertex = HitVertex(xFirst, yFirst)) >= 0)
/* Vertex hit. Toggle selection or select just this */
if (shiftDown) AddSelectVertex(iVertex);
else {
SelectVertex(iVertex);
SelectCnx(-1);
}
else if ((iCnx = HitCnx(xFirst, yFirst)) >= 0)
/* Connection hit. Toggle selection or select just this */
if (shiftDown) AddSelectCnx(iCnx);
else {
SelectCnx(iCnx);
SelectVertex(-1);
}
else {
/* Nothing hit. Unless shiftkey is down, select nothing */
if (!shiftDown) SelectVertex(-1); SelectCnx(-1);
}
DrawWindow();
/* Now track any movement. Don't start moving until the mouse has been
dragged a certain minimum amount. */
xLast = xFirst; yLast = yFirst;
for (trackState = starting;
getbutton(LEFTMOUSE) && trackState != done; ) {
xNew = (getvaluator(MOUSEX) - xOrig) * winWidthFactor;
yNew = (getvaluator(MOUSEY) - yOrig) * winHeightFactor;
switch (trackState) {
case starting:
dist = fabs(xFirst - xNew) + fabs(yFirst - yNew);
if (dist > MINDRAGDIST) trackState = tracking;
break;
case tracking:
MoveSelected(xNew - xLast, yNew - yLast);
DrawWindow();
xLast = xNew; yLast = yNew;
break;
}
}
break;
case addVertices:
AddVertex(xFirst, yFirst);
DrawWindow();
break;
case connectVertices:
/* If vertex already selected, connect new selection to old,
update selection */
iVertex = HitVertex(xFirst, yFirst);
if (iVertex >= 0) {
if (nVSelected)
SelectCnx(AddCnx(selectedVList[0], iVertex));
}
SelectVertex(iVertex);
DrawWindow();
break;
default:
fprintf(stderr, "WHOA! Unknown mode!\n");
break;
}
}
int
AddVertex(float x, float y) {
vertexList[nVertices].x = x;
vertexList[nVertices].y = y;
vertexList[nVertices].color = vColor;
vertexList[nVertices].selected = FALSE;
nVertices++;
return nVertices-1;
}
void
DeleteVertex(int iVertex) {
int i;
/* Shift higher vertex records down */
for (i=iVertex; i<nVertices-1; i++)
vertexList[i] = vertexList[i+1];
nVertices--;
/* Renumber connections referencing higher vertices,
Delete connections referencing this vertex */
for (i=0; i<nCnx; ) {
if (cnxList[i].v0 == iVertex || cnxList[i].v1 == iVertex) DeleteCnx(i);
else {
if (cnxList[i].v0 > iVertex) cnxList[i].v0--;
if (cnxList[i].v1 > iVertex) cnxList[i].v1--;
i++; /* We are keeping this connection, bump to next one */
}
}
/* Delete or renumber selections referencing this vertex */
for (i=0; i<nVSelected; )
if (selectedVList[i] == iVertex) DeleteVSelection(i);
else {
if (selectedVList[i] > iVertex) selectedVList[i]--;
i++;
}
}
int
HitVertex(float x, float y) {
/* Return index of 1st vertex near given point, or -1 if none */
int i;
for (i=0; i<nVertices; i++) {
if (fabs(x - vertexList[i].x) + fabs(y - vertexList[i].y) < MAXHITDIST)
return i;
}
return -1;
}
void
AddSelectVertex(int iVertex) {
/* Toggle membership of iVertex in list of currently selected vertices */
int i, j, found;
/* Check selected list for iVertex. If there, remove it. */
found = FALSE;
for (i=0; i<nVSelected; i++)
if (selectedVList[i] == iVertex) {
DeleteVSelection(i);
vertexList[iVertex].selected = FALSE;
found = TRUE;
break;
}
/* Otherwise, add it */
if (!found) {
selectedVList[nVSelected] = iVertex;
nVSelected++;
vertexList[iVertex].selected = TRUE;
}
}
void
SelectVertex(int iVertex) {
/* Reset the selected vertex list to only this one (-1 => none) */
int i;
/* If already selected, do nothing */
if (vertexList[iVertex].selected) return;
/* Turn off selected flag in all entities but this one */
for (i=0; i<nVSelected; i++)
vertexList[selectedVList[i]].selected = FALSE;
/* Reset the selected list */
if (iVertex >= 0) {
selectedVList[0] = iVertex;
vertexList[iVertex].selected = TRUE;
nVSelected = 1;
} else {
nVSelected = 0;
}
}
void
DeleteVSelection(int iSelect) {
int i;
for (i=iSelect; i<nVSelected-1; i++) selectedVList[i] = selectedVList[i+1];
nVSelected--;
}
void
MoveSelected(float dx, float dy) {
/* Move all selected vertices by given increments */
int i;
for (i = 0; i < nVSelected; i++) {
vertexList[selectedVList[i]].x += dx;
vertexList[selectedVList[i]].y += dy;
}
}
void
DeleteSelectedVertices() {
/* Delete all selected vertices */
int i;
/* Make changes to the vertex list */
for (i=0; i<nVertices; )
if (vertexList[i].selected) DeleteVertex(i);
else i++;
}
int
AddCnx(int v0, int v1) {
int i;
/* Check for degenerate edges */
if (v0 == v1) return -1;
/* Check for this edge already existing */
for (i=0; i<nCnx; i++) {
if (cnxList[i].v0 == v0)
if (cnxList[i].v1 == v1) return i;
else ;
if (cnxList[i].v0 == v1)
if (cnxList[i].v1 == v0) return i;
else ;
}
/* Okay, add it */
cnxList[nCnx].v0 = v0;
cnxList[nCnx].v1 = v1;
cnxList[nCnx].color = cColor;
cnxList[nCnx].selected = FALSE;
nCnx++;
return nCnx-1;
}
void
DeleteCnx(int iCnx) {
/* Delete the given connection */
int i;
/* Shift higher records down */
for (i=iCnx; i<nCnx-1; i++)
cnxList[i] = cnxList[i+1];
nCnx--;
/* Delete or renumber selection info */
for (i=0; i<nCSelected; )
if (selectedCList[i] == iCnx) DeleteCSelection(i);
else {
if (selectedCList[i] > iCnx) selectedCList[i]--;
i++;
}
}
int
HitCnx(float x, float y) {
/* Return index of 1st connection near given point, or -1 if none */
int i, v0, v1;
float dx, dy, len, a, b, c, a2, b2, c2, dist;
for (i=0; i<nCnx; i++) {
v0 = cnxList[i].v0;
v1 = cnxList[i].v1;
dx = vertexList[v1].x - vertexList[v0].x;
dy = vertexList[v1].y - vertexList[v0].y;
len = fsqrt(dx*dx + dy*dy);
a = dy / len; b = -dx / len;
c = -(a*vertexList[v0].x + b*vertexList[v0].y);
dist = a*x + b*y + c;
if (fabs(dist) > MAXHITDIST) continue;
a2 = -b/len; b2 = a/len;
c2 = -(a2*vertexList[v0].x + b2*vertexList[v0].y);
dist = a2*x + b2*y + c2;
if (dist < 0.0 || dist > 1.0) continue;
return i;
}
return -1;
}
void
AddSelectCnx(int iCnx) {
/* Toggle membership of iCnx in list of currently selected connections */
int i, j, found;
/* Check selected list for iCnx. If there, remove it. */
found = FALSE;
for (i=0; i<nCSelected; i++)
if (selectedCList[i] == iCnx) {
DeleteCSelection(i);
cnxList[iCnx].selected = FALSE;
found = TRUE;
break;
}
/* Otherwise, add it */
if (!found) {
selectedCList[nCSelected] = iCnx;
nCSelected++;
cnxList[iCnx].selected = TRUE;
}
}
void
SelectCnx(int iCnx) {
/* Reset the selected connection list to only this one (-1 => none) */
int i;
/* If already selected, do nothing */
if (cnxList[iCnx].selected) return;
/* Turn off selected flag in all connections but this one */
for (i=0; i<nCSelected; i++)
cnxList[selectedCList[i]].selected = FALSE;
/* Reset the selected list */
if (iCnx >= 0) {
selectedCList[0] = iCnx;
cnxList[iCnx].selected = TRUE;
nCSelected = 1;
} else {
nCSelected = 0;
}
}
void
DeleteCSelection(int iSelect) {
int i;
for (i=iSelect; i<nCSelected-1; i++) selectedCList[i] = selectedCList[i+1];
nCSelected--;
}
void
DeleteSelectedCnx() {
/* Delete all selected connections */
int i;
for (i=0; i<nCnx; )
if (cnxList[i].selected) DeleteCnx(i);
else i++;
}
PrintVSelected() {
int i;
fprintf(stdout, "%d Vertices Selected:", nVSelected);
for (i=0; i<nVSelected; i++)
fprintf(stdout, " %d", selectedVList[i]);
fprintf(stdout, "\n");
}
int
AddTriangle(int v0, int v1, int v2) {
triangleList[nTriangles].v0 = v0;
triangleList[nTriangles].v1 = v1;
triangleList[nTriangles].v2 = v2;
return nTriangles++;
}
/****************************************************************************
File I/O
****************************************************************************/
int
ReadLine(FILE *myf, char *buf) {
do {
if (fgets(buf, 120, myf) == NULL) return FALSE;
} while (buf[0] == '#');
return TRUE;
}
int
ReadCnx(char *filename) {
int i;
char line[120];
FILE *f;
/* Open the file and check the type */
if ((f = fopen(filename, "r")) == NULL) {
fprintf(stderr, "Cannot open file %s\n", filename);
return -1;
}
fscanf(f, "%s\n", line);
if (strcmp(line, "2DCnx") != 0) {
fprintf(stderr, "File %s is not a connection file.\n", filename);
return -1;
}
/* Reset the internal structures */
nVertices = nCnx = 0;
nVSelected = nCSelected = 0;
/* Read the vertices, then the connections */
if (!ReadLine(f, line)) goto oops;
sscanf(line, "%d", &nVertices);
for (i=0; i < nVertices; i++) {
if (!ReadLine(f, line)) goto oops;
if (sscanf(line, "%f %f %x",
&vertexList[i].x, &vertexList[i].y, &vertexList[i].color) != 3)
goto oops;
}
if (!ReadLine(f, line)) goto oops;
sscanf(line, "%d", &nCnx);
for (i=0; i < nCnx; i++) {
if (!ReadLine(f, line)) goto oops;
if (sscanf(line, "%d %d %x",
&cnxList[i].v0, &cnxList[i].v1, &cnxList[i].color) != 3)
goto oops;
}
fclose(f);
return 0;
oops:
fclose(f);
fprintf(stderr, "Error reading file %s.\n Input: %s\n", filename, line);
nVertices = nCnx = 0;
return -1;
}
int
WriteCnx(char *filename) {
int i;
FILE *f;
if ((f = fopen(filename, "w")) == NULL) {
fprintf(stderr, "Cannot open output file %s\n", filename);
return FALSE;
}
fprintf(f, "2DCnx\n");
fprintf(f, "%d\n", nVertices);
for (i=0; i<nVertices; i++)
fprintf(f, "%f %f %x\n",
vertexList[i].x, vertexList[i].y, vertexList[i].color);
fprintf(f, "%d\n", nCnx);
for (i=0; i<nCnx; i++)
fprintf(f, "%d %d %x\n",
cnxList[i].v0, cnxList[i].v1, cnxList[i].color);
fclose(f);
}
int
ReadImg(char *filename) {
register IMAGE *image;
register int x, y, xsize, ysize;
register int z, zsize;
short buf[4096];
if ((image=iopen(filename, "r")) == NULL ) {
fprintf(stderr,"readimg: can't open input file %s\n",filename);
return FALSE;
}
xsize = image->xsize;
ysize = image->ysize;
zsize = image->zsize;
if(zsize<3) {
fprintf(stderr,"readimg: this is not an RGB image file\n");
return FALSE;
}
if (bgXsize != xsize || bgYsize != ysize) {
if (bgImage != NULL) free(bgImage);
bgImage = (long *) malloc(xsize * ysize * sizeof(long));
bgXsize = xsize; bgYsize = ysize;
}
for(y=0; y<ysize; y++) {
getrow(image,buf,y,0); /* Red */
for (x=0; x<xsize; x++) bgImage[y*xsize + x] = buf[x] | 0xff000000;
getrow(image,buf,y,1); /* Green */
for (x=0; x<xsize; x++) bgImage[y*xsize + x] |= buf[x] << 8;
getrow(image,buf,y,2); /* Blue */
for (x=0; x<xsize; x++) bgImage[y*xsize + x] |= buf[x] << 16;
}
return TRUE;
}
/******************************************************************************
Menus
******************************************************************************/
MakeMenu() {
freepup(mainMenu);
mainMenu = defpup("Morph Edit %t");
addtopup(mainMenu, "Performance%x14%l");
addtopup(mainMenu, clickMode==selmoveVertices ?
"> Select/Move%x1" : " Select/Move%x1");
addtopup(mainMenu, clickMode==addVertices ?
"> Add Vertices%x2" : " Add Vertices%x2");
addtopup(mainMenu, clickMode==connectVertices ?
"> Add Connections%x3" : " Add Connections%x3");
addtopup(mainMenu, " Draw Triangles%x10%l");
addtopup(mainMenu, "Vertex Color %m", vColorMenu);
addtopup(mainMenu, "Connection Color %m %l", cColorMenu);
addtopup(mainMenu, "Read file blah.rgb%x6");
addtopup(mainMenu, "Read file blah.cnx%x8");
addtopup(mainMenu, "Save as blah.cnx%x7%l");
addtopup(mainMenu, "Add to Target List%x12");
addtopup(mainMenu, "Go to Target %m", targetGotoMenu);
addtopup(mainMenu, "Delete Target from List %m%l", targetDeleteMenu);
addtopup(mainMenu, "Exit%x99");
}
int
DoMenu() {
int item, myDoneFlag;
myDoneFlag = FALSE;
switch (item = dopup(mainMenu)) {
case 1: /* Select/Move Vertices */
clickMode = selmoveVertices;
SelectVertex(-1);
DrawWindow();
MakeMenu();
break;
case 2: /* Add Vertices */
clickMode = addVertices;
SelectVertex(-1);
DrawWindow();
MakeMenu();
break;
case 3: /* Connect Vertices */
clickMode = connectVertices;
SelectVertex(-1);
DrawWindow();
MakeMenu();
break;
/* 4: Vertex Color */
/* 5: Connection Color */
case 6: /* Read Background image */
ReadImg("blah.rgb");
DrawWindow();
break;
case 7: /* Save connection file */
WriteCnx("blah.cnx");
break;
case 8: /* Read connection file */
ReadCnx("blah.cnx");
DrawWindow();
break;
case 10: /* Draw Triangles */
CnxToTri();
DrawWindow();
nTriangles = 0;
break;
/* 11: Goto Target */
case 12: /* Add target to list */
AddTarget();
MakeTargetMenus();
MakeMenu();
break;
/* 13: Delete target from list */
case 14: /* Performance */
performFlag = TRUE;
autoRun = FALSE;
thisTarget = destTarget = -1;
SetPerformGfx();
DrawPerformWindow();
break;
case 99: /* Exit */
myDoneFlag = TRUE;
break;
default:
/* Sub-menus... */
if (item < 20) break;
else if (item < 30)
/* Set Connection Color (color 0 is menu #21) */
cColor = colorList[item - 21];
else if (item < 40)
/* Set Vertex Color (color 0 is menu #31) */
vColor = colorList[item - 31];
else if (item < 60) {
/* Go to a target in the list (target[0] is menu #40) */
UseTarget(item - 40);
SelectVertex(-1);
DrawWindow();
}
else {
/* Delete a target from list (target[0] is menu #60) */
DeleteTarget(item - 60);
MakeTargetMenus();
MakeMenu();
}
break;
}
return myDoneFlag;
}
DrawWindow() {
int i, v0, v1;
long currentColor;
/* Clear the screen */
if (bgImage == NULL) {
cpack(RGB_GRAY);
clear();
} else {
rectzoom(2.0, 2.0);
lrectwrite(0, 0, 255, 255, bgImage);
}
/* Draw the triangles */
currentColor = 0;
for (i=0; i<nTriangles; i++) {
currentColor++;
currentColor = currentColor % (sizeof(triColorList)/sizeof(long));
cpack(triColorList[currentColor]);
bgnpolygon();
v2f(&vertexList[triangleList[i].v0].x);
v2f(&vertexList[triangleList[i].v1].x);
v2f(&vertexList[triangleList[i].v2].x);
endpolygon();
}
/* Draw the Connections */
for (i=0; i<nCnx; i++) {
cpack(cnxList[i].color + (cnxList[i].selected ? RGB_BRIGHTER : 0));
v0 = cnxList[i].v0;
v1 = cnxList[i].v1;
move2(vertexList[v0].x, vertexList[v0].y);
draw2(vertexList[v1].x, vertexList[v1].y);
}
/* Draw the Vertices */
for (i=0; i<nVertices; i++) {
cpack(vertexList[i].color + (vertexList[i].selected?RGB_BRIGHTER:0));
move2(vertexList[i].x, vertexList[i].y);
rmv2(-crossArm, 0.0);
rdr2(crossArm + crossArm, 0.0);
rmv2(-crossArm, -crossArm);
rdr2(0.0, crossArm + crossArm);
}
swapbuffers();
}
/*************************************************************************
Connect to triangle rep routines
*************************************************************************/
void
XRefEdge(int iVertex, int iCnx);
int
NextEdge(int iVertex, int iEdge);
int
OtherEnd(int iEdge, int iVertex);
int
CompareTheta(float *th0, float *th1) {
return (*th0 > *th1) ? -1 : 1;
}
void
CnxToTri() {
/* My own vertex-edge list for sorting */
struct mel_tag {
float theta;
int edge;
} myEdgeList[MAXEDGESPERVERTEX];
int i, j, k, myNEdges, myObtuse;
int v0, v1, v2, v3, e0, e1, e2;
float dTheta;
/* Find all the edges connected to each vertex */
for (i=0; i<nVertices; i++) vertexEdges[i].nEdges = 0;
for (i=0; i<nCnx; i++) {
XRefEdge(cnxList[i].v0, i);
XRefEdge(cnxList[i].v1, i);
}
/* Measure and order edges at each vertex. */
for (i=0; i<nVertices; i++) {
/* Collect the edges, and sort them into clockwise order */
if ((myNEdges = vertexEdges[i].nEdges) <= 0) continue;
for (j=0; j<myNEdges; j++) {
myEdgeList[j].edge = vertexEdges[i].edge[j].index;
v0 = cnxList[myEdgeList[j].edge].v0;
v1 = cnxList[myEdgeList[j].edge].v1;
myEdgeList[j].theta = fatan2(
vertexList[v0].y - vertexList[v1].y,
vertexList[v0].x - vertexList[v1].x);
if (v0 != i)
if (myEdgeList[j].theta < 0.0)
myEdgeList[j].theta += M_PI;
else
myEdgeList[j].theta -= M_PI;
}
qsort((char *) myEdgeList, myNEdges, sizeof(struct mel_tag),
CompareTheta);
/* Copy edges back into vertexEdge in clockwise order.
Mark obtuse wedges radiating from vertex */
for (j=0; j<myNEdges-1; j++) {
dTheta = myEdgeList[j].theta - myEdgeList[j+1].theta;
if (dTheta < 0.0) dTheta += M_PI + M_PI;
vertexEdges[i].edge[j].marked = dTheta > M_PI;
vertexEdges[i].edge[j].index = myEdgeList[j].edge;
}
dTheta = (myEdgeList[myNEdges-1].theta - myEdgeList[0].theta);
if (dTheta < 0.0) dTheta += M_PI + M_PI;
vertexEdges[i].edge[myNEdges-1].marked = dTheta > M_PI;
vertexEdges[i].edge[myNEdges-1].index = myEdgeList[myNEdges-1].edge;
}
/* For each vertex, try to make triangles out of every edge */
nTriangles = 0;
for (i=0; i<nVertices; i++) {
v0 = i;
for (j=0; j<vertexEdges[i].nEdges; j++) {
if ((e0 = NextEdge(v0, vertexEdges[i].edge[j].index)) < 0) continue;
v1 = OtherEnd(e0, v0);
if ((e1 = NextEdge(v1, e0)) < 0) continue;
v2 = OtherEnd(e1, v1);
if ((e2 = NextEdge(v2, e1)) < 0) continue;
v3 = OtherEnd(e2, v2);
if (v3 == v0) AddTriangle(v0, v1, v2);
}
}
}
void
XRefEdge(int iVertex, int iCnx) {
/* Add this vertex-edge pair to the per-vertex list */
int n;
n = vertexEdges[iVertex].nEdges;
vertexEdges[iVertex].edge[n].index = iCnx;
vertexEdges[iVertex].nEdges++;
}
int
NextEdge(int iVertex, int iEdge) {
/* Get the edge out if vertex iVertex which follows edge iEdge in
clockwise order.
Return false if that wedge is already marked. Otherwise, mark it. */
int i;
for (i=0; i<vertexEdges[iVertex].nEdges; i++) {
if (vertexEdges[iVertex].edge[i].index == iEdge) {
/* Found it. Marked already? */
if (vertexEdges[iVertex].edge[i].marked)
return -1;
else {
/* Not marked.
Mark it, then return next one, or 0th one if we're at end */
vertexEdges[iVertex].edge[i].marked = TRUE;
if (i<vertexEdges[iVertex].nEdges-1)
return vertexEdges[iVertex].edge[i+1].index;
else
return vertexEdges[iVertex].edge[0].index;
}
}
}
fprintf(stderr,
"Data problems in NextEdge, vertex %d, edge %d\n", iVertex, iEdge);
return -1;
}
int
OtherEnd(int iEdge, int iVertex) {
/* Find the vertex at the other end of edge iEdge from iVertex */
if (cnxList[iEdge].v0 == iVertex) return cnxList[iEdge].v1;
else return cnxList[iEdge].v0;
}
/*****************************************************************************
Target management
*****************************************************************************/
AddTarget() {
int i, j;
if (nTargets >= MAXTARGETS) return;
/* Make a copy of the Texture Image */
target[nTargets].image = (long *) malloc(sizeof(long) * bgXsize * bgYsize);
target[nTargets].xSize = bgXsize;
target[nTargets].ySize = bgYsize;
for (i=0; i<bgXsize*bgYsize; i++)
target[nTargets].image[i] = bgImage[i];
/* Make a copy of the vertex list */
target[nTargets].vertexList =
(struct vertlist_tag *) malloc(sizeof(struct vertlist_tag) * nVertices);
target[nTargets].nVertices = nVertices;
for (i=0; i<nVertices; i++)
target[nTargets].vertexList[i] = vertexList[i];
/* Make a copy of the connection list (just to keep around ) */
target[nTargets].cnxList =
(struct cnxlist_tag *) malloc(sizeof(struct cnxlist_tag) * nCnx);
target[nTargets].nCnx = nCnx;
for (i=0; i<nCnx; i++)
target[nTargets].cnxList[i] = cnxList[i];
/* Get a current triangulization and copy it too */
CnxToTri();
target[nTargets].triangleList =
(struct tri_tag *) malloc(sizeof(struct tri_tag) * nTriangles);
target[nTargets].nTriangles = nTriangles;
for (i=0; i<nTriangles; i++)
target[nTargets].triangleList[i] = triangleList[i];
nTriangles = 0;
/* Assign a texture id */
for (i=1; i<=MAXTARGETS; i++) {
for (j=0; j<nTargets; j++)
if (target[j].textureIndex == i) break;
if (j >= nTargets) {
/* i is an unused index. Assign it. */
target[nTargets].textureIndex = i;
break;
}
}
nTargets++;
}
UseTarget(int iTarget) {
/* Make one of the existing targets the current working image/cnx combo */
int i;
/* Reallocate image array if the dimensions are different */
if (target[iTarget].xSize != bgXsize || target[iTarget].ySize != bgYsize) {
free(bgImage);
bgXsize = target[iTarget].xSize;
bgYsize = target[iTarget].ySize;
bgImage = (long *) malloc(sizeof(long) * bgXsize * bgYsize);
}
for (i=0; i<bgXsize*bgYsize; i++)
bgImage[i] = target[iTarget].image[i];
/* Copy the vertex list back */
nVertices = target[iTarget].nVertices;
for (i=0; i<nVertices; i++) vertexList[i] = target[iTarget].vertexList[i];
/* Copy the connection list back */
nCnx = target[iTarget].nCnx;
for (i=0; i<nCnx; i++) cnxList[i] = target[iTarget].cnxList[i];
}
DeleteTarget(int iTarget) {
/* Free the memory for the indicated target, and collapse the list */
int i;
free(target[iTarget].image);
free(target[iTarget].vertexList);
free(target[iTarget].cnxList);
free(target[iTarget].triangleList);
for (i=iTarget; i<nTargets-1; i++) target[i] = target[i+1];
nTargets--;
}
MakeTargetMenus() {
int i;
char buf[30];
freepup(targetGotoMenu);
targetGotoMenu = defpup("Go to Target:%t");
for (i=0; i<nTargets; i++) {
sprintf(buf, "Target %d %%x%d", i+1, 40+i);
addtopup(targetGotoMenu, buf, 0);
}
freepup(targetDeleteMenu);
targetDeleteMenu = defpup("Delete Target:%t");
for (i=0; i<nTargets; i++) {
sprintf(buf, "Target %d %%x%d", i+1, 60+i);
addtopup(targetDeleteMenu, buf, 0);
}
}
AnimateTransition() {
int i, iVertex, iFrame, nFrames;
float p, f, omf;
/* Bob Drebin's workaround hack for 3.3.2 texture management bug */
/* This code causes flashes on my PI, even resetting RGBmode & doublebuffer
tevbind(TV_ENV0, 0);
acsize(16);
gconfig();
acsize(0);
gconfig();
tevbind(TV_ENV0, 1);
*/
/* Set up our vertices once if no morphing happens */
if (thisTarget<0) {
if (destTarget<0) return;
nTmpVertices = target[destTarget].nVertices;
for (iVertex=0; iVertex<nTmpVertices; iVertex++) {
tmpVertexList[iVertex].x =
target[destTarget].vertexList[iVertex].x;
tmpVertexList[iVertex].y =
target[destTarget].vertexList[iVertex].y;
}
}
/* Loop through the frames */
nFrames = frameCount[currentSpeed];
if ((thisTarget<0 || destTarget<0) && showTwins) nFrames = 1;
for (iFrame=1; !flushQueue && iFrame<=nFrames; iFrame++) {
p = iFrame / (float) nFrames;
f = (-p-p + 3.0)*p*p;
if (flushQueue = getbutton(LEFTCTRLKEY)) f = 1.0;
omf = 1.0 - f;
/* Do a cross-dissolve with alpha values */
thisTargetColor = destTargetColor = RGB_WHITE;
if (!showTwins) {
thisTargetColor |= ((int)(255*omf)) <<24;
destTargetColor |= ((int)(255* f)) <<24;
} else {
thisTargetColor |= 255 << 24;
destTargetColor |= 255 << 24;
}
/* Move the vertices */
if (thisTarget>=0 && destTarget>=0) {
for (iVertex=0; iVertex<nTmpVertices; iVertex++) {
tmpVertexList[iVertex].x =
(1.0-f) * target[thisTarget].vertexList[iVertex].x +
( f) * target[destTarget].vertexList[iVertex].x;
tmpVertexList[iVertex].y =
(1.0-f) * target[thisTarget].vertexList[iVertex].y +
( f) * target[destTarget].vertexList[iVertex].y;
}
}
DrawPerformWindow();
}
}
DrawPerformWindow() {
struct vertlist_tag *vl, *tvl;
struct tri_tag *tl;
int nv, nt, iTri;
/* Clear the screen */
reshapeviewport();
cpack(RGB_BLACK);
clear();
if (performAspect > 1.0)
/* Higher than wide, fit image side-to-side */
ortho2(0.0, 1.0,
0.0 - (performAspect-1.0)/2.0, 1.0 + (performAspect-1.0)/2.0);
else
/* Wider than high, fit top-to-bottom */
ortho2(0.0 - (1.0/performAspect-1.0)/2.0,
1.0 + (1.0/performAspect-1.0)/2.0,
0.0, 1.0);
/* Draw this target image */
if (thisTarget>=0) {
/* If showTwins is set, show this image in the left half of window */
if (showTwins) viewport(0, performXSize/2, 0, performYSize);
texbind(TX_TEXTURE_0, target[thisTarget].textureIndex);
cpack(thisTargetColor);
vl = tmpVertexList;
nv = nTmpVertices;
tvl = target[thisTarget].vertexList;
tl = target[thisTarget].triangleList;
nt = target[thisTarget].nTriangles;
for (iTri=0; iTri<nt; iTri++) {
bgnpolygon();
t2f(&tvl[tl[iTri].v0].x);
v2f( &vl[tl[iTri].v0].x);
t2f(&tvl[tl[iTri].v1].x);
v2f( &vl[tl[iTri].v1].x);
t2f(&tvl[tl[iTri].v2].x);
v2f( &vl[tl[iTri].v2].x);
endpolygon();
}
if (showMesh && (showTwins || destTarget<0)) DrawMesh(thisTarget, vl);
}
/* Draw destination target image */
if (destTarget>=0) {
/* If showTwins is set, show this image in the right half of window */
if (showTwins)
viewport(performXSize/2+1, performXSize, 0, performYSize);
texbind(TX_TEXTURE_0, target[destTarget].textureIndex);
cpack(destTargetColor);
vl = tmpVertexList;
nv = nTmpVertices;
tvl = target[destTarget].vertexList;
tl = target[destTarget].triangleList;
nt = target[destTarget].nTriangles;
for (iTri=0; iTri<nt; iTri++) {
bgnpolygon();
t2f(&tvl[tl[iTri].v0].x);
v2f( &vl[tl[iTri].v0].x);
t2f(&tvl[tl[iTri].v1].x);
v2f( &vl[tl[iTri].v1].x);
t2f(&tvl[tl[iTri].v2].x);
v2f( &vl[tl[iTri].v2].x);
endpolygon();
}
if (showMesh) DrawMesh(destTarget, vl);
}
swapbuffers();
}
DrawMesh(int iTarget, struct vertlist_tag *vl) {
int i, v0, v1;
struct target_tag *t;
t = &target[iTarget];
texbind(TX_TEXTURE_0, 0);
/* Draw the Connections */
for (i=0; i<t->nCnx; i++) {
cpack(t->cnxList[i].color | 255<<24);
v0 = t->cnxList[i].v0;
v1 = t->cnxList[i].v1;
move2(vl[v0].x, vl[v0].y);
draw2(vl[v1].x, vl[v1].y);
}
/* Draw the Vertices */
for (i=0; i<t->nVertices; i++) {
cpack(t->vertexList[i].color | 255<<24);
move2(vl[i].x, vl[i].y);
rmv2(-crossArm, 0.0);
rdr2(crossArm + crossArm, 0.0);
rmv2(-crossArm, -crossArm);
rdr2(0.0, crossArm + crossArm);
}
}